﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace SimpleORM
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    public sealed class DataTableAttribute : Attribute
    {
        #region Properties

        public string FullName { get { return string.Format("{0}.{1}", Schema, TableName); } }
        public string TableName { get; set; }
        public string Schema { get; set; }

        public string AliasName
        {
            get { return string.IsNullOrWhiteSpace(_AliasName) ? TableName : _AliasName; }
            set { _AliasName = value; }
        }

        #endregion

        #region Fields

        private string _AliasName;

        #endregion

        #region Ctor

        public DataTableAttribute()
            : this(string.Empty)
        {
        }

        public DataTableAttribute(string name)
            : this("dbo", name)
        {
        }

        public DataTableAttribute(string schema, string name)
        {
            Schema = schema;
            TableName = name;
        }

        #endregion

        #region Methods

        public bool Equals(string schema, string name)
        {
            return Schema == schema && TableName == name;
        }

        public override string ToString()
        {
            return FullName;
        }

        #endregion
    }

    public class ElementRelationship : CDObject
    {
        #region Properties

        public string Primary { get { return string.Format("{0}.{1}.{2}", PrimarySchema, PrimaryElementName, PrimaryColumnName); } }
        public string Foreign { get { return string.Format("{0}.{1}.{2}", ForeignSchema, ForeignElementName, ForeignColumnName); } }

        public string PrimarySchema { get; set; }
        public string PrimaryElementName { get; set; }
        public string PrimaryColumnName { get; set; }
        public string ForeignSchema { get; set; }
        public string ForeignElementName { get; set; }
        public string ForeignColumnName { get; set; }

        #endregion

        #region Methods

        public bool GetRelationship(string schema, string elementName, out string anotherSchema, out string anotherElementName)
        {
            if (schema == PrimarySchema && elementName == PrimaryElementName)
            {
                anotherSchema = ForeignSchema;
                anotherElementName = ForeignElementName;
                return true;
            }
            else if (schema == ForeignSchema && elementName == ForeignElementName)
            {
                anotherSchema = PrimarySchema;
                anotherElementName = PrimaryElementName;
                return true;
            }
            else
            {
                anotherSchema = null;
                anotherElementName = null;
                return false;
            }
        }

        public bool Contains(string schema, string elementName)
        {
            return Primarys(schema, elementName) || Foreigns(schema, elementName);
        }

        public bool Primarys(string schema, string elementName)
        {
            return PrimarySchema == schema && PrimaryElementName == elementName;
        }

        public bool Foreigns(string schema, string elementName)
        {
            return ForeignSchema == schema && ForeignElementName == elementName;
        }

        public override string ToString()
        {
            return string.Format("{0} -> {1}", Primary, Foreign);
        }

        #endregion
    }
    public interface IConnection
    {
        #region Methods

        void OpenConnection();
        void CloseConnection();

        #endregion
    }
    public interface IDataSource : ICD
    {
        #region Properties

        string ProviderName { get; }
        string ConnectionString { get; }
        Guid ID { get; }

        #endregion

        #region Methods

        IDataSourceSchema CreateSchema();

        #endregion
    }
    public interface ISaveChanges
    {
        #region Methods

        object SaveChanges();

        #endregion
    }
    public interface ITransaction
    {
        #region Methods

        void BeginTransaction();
        void CommitTransaction();
        void RollbackTransaction();

        #endregion
    }
    public class QueryCollection : CDObjectList<IQueryable>
    {
        #region Fields

        private IProviderDatabase provider;

        #endregion

        #region Ctor

        public QueryCollection(IProviderDatabase provider)
        {
            this.provider = provider;
        }

        #endregion

        #region Methods

        #region Methods - Save

        public object Save()
        {
            lock (this)
            {
                if (Count == 0)
                    return 0;

                object result = provider.IsSupportBatch ? SaveBatch() : SaveNoBatch();

                Clear();

                return result;
            }
        }

        private object SaveNoBatch()
        {
            for (int i = 0; i < Count; i++)
                this[i].Save();

            return Count;
        }

        private object SaveBatch()
        {
            IQueryable qc = this[0];
            for (int i = 1; i < Count; i++)
                qc.Append(this[i]);

            return qc.Save();
        }

        #endregion

        #region Methods - Override

        public override object Clone()
        {
            return base.Clone();
        }

        #endregion

        #endregion
    }
    public interface IQuerybase<T> : IQuerybase
    {
        #region Methods

        #region Methods - Execute

        //[OutputCommand]
        //[OpenConnection]
        //[CloseConnection]
        //[GetDbCache]
        //[SetDbCache]
        new T Execute();

        //[OutputCommand]
        //[OpenConnection]
        //[CloseConnection]
        //[BeginTransaction]
        //[Commit]
        //[RollBack]
        //[GetDbCache]
        //[SetDbCache]
        new T ExecuteInTransaction();

        //[OutputCommand]
        //[OpenConnection]
        //[CloseConnection]
        //[GetDbCache]
        //[SetDbCache]
        List<T> GetObjects();

        #endregion

        #endregion
    }
    public interface IQueryContext<T> : IQueryContext, IQuerybase<T>, IOrderedQueryable<T>
    {
        #region Methods

        #region Methods - Combine

        IQueryContext<T> Combine(IQueryContext<T> qc);

        #endregion

        #endregion
    }
    public interface IDbContext : IDataSource, IConnection, ITransaction, ISaveChanges
    {
        #region Properties

        IProviderDatabase DataSource { get; }
        QuerySettings Settings { get; }
        QueryCollection Queries { get; }

        #endregion

        #region Methods

        #region Methods - Create

        IQueryContext CreateQuery();
        IQueryContext CreateQuery(Type type);
        IQueryContext<T> CreateQuery<T>();

        IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                            object executeArgument,
                                                            Expression<Func<TResult>> model);
        IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                            object executeArgument);
        IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                            object executeArgument,
                                                            eCommandType commandType,
                                                            Expression<Func<TResult>> model);
        IProcedureContext<TResult> CreateProcedure<TResult>(string procedureName,
                                                            object executeArgument,
                                                            eCommandType commandType);

        #endregion

        #region Methods - Cache

        void ClearCache();

        #endregion

        #endregion
    }

    public interface IProcedureContext<T> : IQuerybase<T>
    {
        #region Methods

        DParameter CreateParameter(string paramName, object paramValue);
        DParameter CreateParameter(string paramName, object paramValue, object paramType);

        DParameter AddParameter(string paramName, object paramValue);
        DParameter AddParameter(string paramName, object paramValue, object paramType);

        #endregion
    }
}
